﻿// LearnOpenGLProject.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <glad/glad.h>
#include "GLPipeline.h"
#include "Shader.h"
#include "ImageLoader.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
//#include "Vertices.h"
#include "stb_image.h"
#include "Camera.h"

/*
unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

unsigned int VAO, VBO, EBO;

// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;	// time between current frame and last frame
float lastFrame = 0.0f;

/// <summary>
/// VAO VBO EBO Set.
/// </summary>
void CubeVAOSet();

void LightVAOSet();

/// <summary>
/// Handle input
/// </summary>
/// <param name="window"></param>
/// <param name="key"></param>
/// <param name="scancode"></param>
/// <param name="action"></param>
/// <param name="mods"></param>
void ProcessInput(GLFWwindow* window);
/// <summary>
/// mouse call back function
/// </summary>
/// <param name="window"></param>
/// <param name="xpos"></param>
/// <param name="ypos"></param>
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
/// <summary>
/// scroll call back function
/// </summary>
/// <param name="window"></param>
/// <param name="xoffset"></param>
/// <param name="yoffset"></param>
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);


int main_2()
{
    //Init
    GLPipeline glPipeline;
    glPipeline.init();

    //base config
    glfwSetInputMode(glPipeline.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glEnable(GL_DEPTH_TEST);
    glfwSetCursorPosCallback(glPipeline.window, mouse_callback);
    glfwSetScrollCallback(glPipeline.window, scroll_callback);


    CubeVAOSet();
    Shader CurrentShader("GLSLCode\\shader.vs", "GLSLCode\\shader.fs");

    float HorizontalOffset = 0.5f;
    ImageLoader ImageLoaderInstance;
    ImageLoaderInstance.InitImage();
   
    CurrentShader.UseShader();

    

    CurrentShader.setInt("texture1", 0); // 使用着色器类设置
    CurrentShader.setInt("texture2", 1); // 使用着色器类设置
    
    // pass projection matrix to shader (as projection matrix rarely changes there's no need to do this per frame)
    // -----------------------------------------------------------------------------------------------------------
    //glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    //CurrentShader.setMat4("projection", projection);

    

    //glm::mat4 view = glm::mat4(1.0f);;
    //// 注意，我们将矩阵向我们要进行移动场景的反方向移动。
    //view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

    //glm::mat4 projection = glm::mat4(1.0f);;
    //projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

    //// create transformations
    //CurrentShader.setMat4("model", model);
    //glUniformMatrix4fv(glGetUniformLocation(CurrentShader.GetShaderID(), "view"), 1, GL_FALSE, &view[0][0]);

//    CurrentShader.setMat4("projection", projection);

    while (!glfwWindowShouldClose(glPipeline.window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;
        // 清除颜色缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        //use shader program
        ProcessInput(glPipeline.window);

        ////画三角形
        //{
        //    //通过uniform 更改fragment的输出颜色
        //    float UseColor = (sin(glfwGetTime()) / 2.f) + 0.5f;
        //    int vertexColorLocation = glGetUniformLocation(CurrentShader.GetShaderID(), "UseColor");
        //    //值若为-1的话，则代表获取uniform变量地址失败
        //    if (vertexColorLocation != -1)
        //        glUniform4f(vertexColorLocation, 0.0f, UseColor, 0.0f, 1.0f);

        //    CurrentShader.setFloat("HorizontalOffset", 0.5f);

        //    //绘制图形
        //    glDrawArrays(GL_TRIANGLES, 0, 3);
        //      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        //}
        
        //画图片
        {
            
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, ImageLoaderInstance.GetTextureRef(0));
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, ImageLoaderInstance.GetTextureRef(1));
            CurrentShader.UseShader();


            // create transformations
             // make sure to initialize matrix to identity matrix first
            
            glm::mat4 projection = glm::mat4(1.0f);

            float radius = 10.0f;
            float camX = sin(glfwGetTime()) * radius;
            float camZ = cos(glfwGetTime()) * radius;
            glm::mat4 view = glm::mat4(1.0f);
            view = camera.GetViewMatrix();
            projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

            // retrieve the matrix uniform locations

            
            // note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
            CurrentShader.setMat4("projection", projection);

            // pass them to the shaders (3 different ways)
            unsigned int viewLoc = glGetUniformLocation(CurrentShader.GetShaderID(), "view");
            glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &view[0][0]);
            
            // render boxes
            glBindVertexArray(VAO);
            for(unsigned int i = 0; i < 10; i++)
            {
                glm::mat4 model = glm::mat4(1.0f);
                model = glm::translate(model, Vertices::cubePositions[i]);
                float angle = 20 *i;
                if (i % 3 == 0) {
                    model = glm::rotate(model, (GLfloat)glfwGetTime(), glm::vec3(1.0f, 0.3f, 0.5f));
                }
                else {
                    model = glm::rotate(model, glm::radians(angle) * (GLfloat)glfwGetTime(), glm::vec3(5.0f, 1.0f, 2.0f));
                }
             //   model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
                CurrentShader.setMat4("model", model);

                glDrawArrays(GL_TRIANGLES, 0, 36);
            }
            

        //    glBindVertexArray(VAO);
        //    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        }
          // 交换缓冲并查询IO事件
        glfwSwapBuffers(glPipeline.window);
        glfwPollEvents();
    }
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
//    glDeleteBuffers(1, &EBO);
    CurrentShader.DeleteShaderProgram();
    glfwDestroyWindow(glPipeline.window);
    glfwTerminate();
    return 0;
}


void CubeVAOSet()
{
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
//    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    //往显卡写VBO，分配显存空间GL_STATIC_DRAW表示，值不修改
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices::CubeVertices), Vertices::CubeVertices, GL_STATIC_DRAW);
    //    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
        //分配EBO
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

//    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    //顶点信息结构
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //颜色信息结构
//    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), ((void*)(3 * sizeof(float))));
//    glEnableVertexAttribArray(1);

    //纹理信息结构
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), ((void*)(3 * sizeof(float))));
    glEnableVertexAttribArray(1);

}

void LightVAOSet()
{
    unsigned int lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // 只需要绑定VBO不用再次设置VBO的数据，因为箱子的VBO数据中已经包含了正确的立方体顶点数据
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 设置灯立方体的顶点属性（对我们的灯来说仅仅只有位置数据）
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
}


void ProcessInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}
*/